\documentclass[12pt,a4paper,utf8]{article}


\usepackage[frenchb]{babel}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage {listings}
\usepackage{graphicx}
\usepackage{amsmath,amssymb}
\usepackage{xcolor}
\usepackage[absolute]{textpos}

\title{Projet web}
\author{GOUY Paul \\ KAESTLE Vincent \\ KOECK Paul-Henri}
\date{mai 2012}

\lstset{ 
language=SQL,
basicstyle=\ttfamily\scriptsize
}

\begin{document}

\maketitle
\clearpage
\tableofcontents
\clearpage

\section*{Introduction}
Nous avons choisi de développer ce projet sous Symfony 2. La raison est bien simple: Symfony 2 est l'avenir du framework Symfony.

Dans Symfony 2, tout est bundle: le coeur même du framework est composé de bundles. C'est une des différences fondamentales entre la nouvelle et l'ancienne version. Cela apporte une grande flexibilité au développement d'applications Symfony 2, même si ce concept suppose d'avoir globalement une connaissance plus précise du framework. Cela explique pourquoi une grande partie de la réalisation de ce projet a consisté à lire la documentation de Symfony 2.

La structure de notre projet comporte deux bundles: l'un est chargé de gérer les utilisateurs, et l'autre s'occupe de l'interface marchande du site web. L'application est appelée \textbf{\emph{"KGK"}}, les bundles se trouvent donc dans le dossier \emph{src/KGK}.

\section{FOSUserBundle}

Le projet doit gérer trois types d'utilisateurs différents: les utilisateurs anonymes, les utilisateurs authentifiés et les administrateurs.
La plupart des projets Symfony 2 utilisent le bundle \emph{FOSUserBundle}. Comme pour tous les bundles, on l'active dans le fichier app/AppKernel.php.

\subsection{Paramétrage de base}
Le paramétrage du fichier app/config/security.yml permet d'indiquer au composant sécurité de Symfony d'utiliser le FOSUserBundle. Voici le contenu de ce fichier:
\begin{verbatim}
security:
    providers:
        fos_userbundle:
            id: fos_user.user_manager

    encoders:
        "FOS\UserBundle\Model\UserInterface": sha512

    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: ^/
            form_login:
                provider: fos_userbundle
                always_use_default_target_path: true
                default_target_path: /store
            logout:       true
            anonymous:    true

    access_control:
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/, role: ROLE_ADMIN }

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN
\end{verbatim}

On paramètre ensuite le routage du bundle, directement dans app/config/routing.yml.

\begin{verbatim}
fos_user_security:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"

fos_user_profile:
    resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
    prefix: /profile

fos_user_register:
    resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
    prefix: /register

fos_user_resetting:
    resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
    prefix: /resetting

fos_user_change_password:
    resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
    prefix: /profile
\end{verbatim}

\subsection{Stockage des utilisateurs dans la base de données}
L'ORM utilisée par le projet est Doctrine. Naturellement, notre classe \emph{User} se trouve dans le namespace \emph{Entity} de notre Bundle User. Cette classe hérite de la classe \emph{User} du FOSUserBundle, qui contient les attributs de base suivants:
\begin{itemize}
 \item \textbf{\$id}: id de l'utilisateur dans la base de données
 \item \textbf{\$username}: nom d'utilisateur
 \item \textbf{\$usernameCanonical}: nom d'utilisateur canonique (en minuscules), peut servir en fonctionnement interne du FOSUserBundle
 \item \textbf{\$email}: adresse e-mail de l'utilisateur
 \item \textbf{\$emailCanonical}: adresse e-mail canonique (en minuscules)
 \item \textbf{\$enabled}: flag de confirmation de l'inscription du compte utilisateur (utilisé lors s'il y a confirmation d'inscription par email)
 \item \textbf{\$password}: mot de passe encodé
 \item \textbf{\$salt}: salt d'encodage
 \item \textbf{\$plainPassword}: mot de passe non encodé, n'est pas enregistré dans la base mais peut servir à des opérations de validation
 \item \textbf{\$lastlogin}: date de dernière connexion
 \item \textbf{\$confirmationToken}: token de confirmation (généré par le bundle et fourni par le navigateur pour sécuriser la connexion)
 \item \textbf{\$roles}: tableau qui définit les roles d'un utilisateur. Un administrateur dispose du rôle ROLE\_ADMIN, et un simple utilisateur dispose du rôle ROLE\_USER.
 \item \textbf{\$groups}: collection de groupes, peut servir lorsque la base de données gère différents groupes d'utilisateurs
 \item \textbf{\$expired}: flag d'expiration d'un compte
 \item \textbf{\$expiresAt}: date d'expiration du compte
 \item \textbf{\$credentialsExpired}: flag d'expiration des informations de connexion de l'utilisateur
 \item \textbf{\$credentialsExpireAt}: date d'expiration des informations de connexion de l'utilisateur
 \item \textbf{\$locked}: flag de désactivation d'un compte
\end{itemize}

\vspace{1em}

Dans le dossier Entity de notre bundle User, on a donc créé un fichier User.php qui définit notre classe User. Pour les besoins du projet, on définit les attributs suivants:
\begin{itemize}
\item \textbf{\$nom}
\item \textbf{\$prenom}
\item \textbf{\$adresse}
\item \textbf{\$code\_postal}
\item \textbf{\$ville}
\item \textbf{\$telephone}
\end{itemize}

\vspace{1em}

En annotation, on définit des contraintes de validation. Par exemple, l'attribut \textbf{\$prenom} doit être non nul et doit avoir une taille comprise entre 3 et 64 caractères.

On a aussi généré les setters et les getters, ainsi que les annotations qui définissent le type de champ de chaque attribut dans la base. Par exemple, l'attribut \textbf{\$prenom} est représenté dans la base par un champ string de taille 64.

Pour finir, on configure dans le fichier app/config.yml la classe User utilisée le FOSUserBundle.

\begin{verbatim}
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: KGK\UserBundle\Entity\User
\end{verbatim}

\subsection{Gestion des inscriptions et attribution d'un mot de passe aléatoire}

Le FOSUserBundle gère les inscriptions d'utilisateur. Lorsque l'on se rend sur la route \textbf{fos\_user\_register}, on peut déjà s'inscrire et créer un utilisateur. Cependant, cette inscription n'est pas en adéquation avec le sujet du projet. Il manque:
\begin{itemize}
\item Les champs du formulaire qui correspondent aux attributs rajoutés
\item La génération aléatoire de mot de passe
\end{itemize}

Le FOSUserBundle est prévu de telle manière que c'est l'utilisateur lui-même qui définit son mot de passe. C'est une caractéristique du bundle qui lui est très liée: enlever la définition du mot de passe par l'utilisateur est \textbf{très} pénible. Dans un premier temps, on ne s'occupe pas du mot de passe.

\subsubsection{Ajout des champs dans le formulaire}
Il suffit de créer dans le dossier Form/Type de notre bundle un fichier RegistrationFormType qui hérite du fichier RegistrationFormType du FOSUserBundle et qui ajoute les champs désirés dans le constructeur de formulaire.

Ensuite, on configure ue service kgk\_user\_registration qui fournit au FOSUserBundle notre formulaire personnalisé. On crée donc un fichier services.xml dans le dossier Resources/config de notre bundle:
\begin{verbatim}
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
    http://symfony.com/schema/dic/services/services-1.0.xsd">
    <services>
        <service id="kgk_user.registration.form.type"
        class="KGK\UserBundle\Form\Type\RegistrationFormType">
            <tag name="form.type" alias="kgk_user_registration" />
            <argument>%fos_user.model.user.class%</argument>
        </service>
    </services>
</container>

\end{verbatim}


Évidemment, il faut rajouter ce service dans le fichier config.yml:
\begin{verbatim}
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: KGK\UserBundle\Entity\User
    registration:
        form:
            type: kgk_user_registration
\end{verbatim}

Maintenant, la page d'inscription affiche un formulaire qui correspond au sujet (à l'exception du mot de passe) et qui permet de créer un compte.

\subsubsection{Attribution d'un mot de passe aléatoire}
Le FOSUserBundle s'attend à ce que l'utilisateur fournisse un mot de passe. Si l'on se contente de supprimer le mot de passe du formulaire, le service de validation de formulaire renvoie une erreur. Il faut donc modifier ce service de validation.

En réalité, l'utilisation correcte du bundle voudrait qu'on crée une nouveau groupe de validation KGK\_Register qui serait une copie exacte du groupe de validation Register que possède le FOSUserBundle, à l'exception du fait que le champ mot de passe est ignoré.

Il est plus rapide (et un peu moins propre) de commenter les lignes concernant la validation du mot de passe dans le fichier validation.xml situé dans le dossier Resources/config du FOSUserBundle.

Ensuite, on a besoin de réécrire le fichier RegistrationFormType de notre Bundle: il hérite du fichier RegistrationFormType du FOSUserBundle, qui prévoit que l'utilisateur rentre un mot de passe.

On doit également modifier le contrôleur qui manipule la page d'inscription, ainsi que le form handler associé, qui persiste les utilisateurs dans la base.

Créer un nouveau form handler suppose de rajouter un service dans le fichier validation.xml de notre bundle et dans le fichier app./config/config.yml.

Dans services.xml, on rajoute:
\begin{verbatim}
<service id="kgk_user.form.handler.registration"
class="KGK\UserBundle\Form\Handler\RegistrationFormHandler"
scope="request" public="false">
    <argument type="service" id="fos_user.registration.form" />
    <argument type="service" id="request" />
    <argument type="service" id="fos_user.user_manager" />
    <argument type="service" id="fos_user.mailer" />
</service>
\end{verbatim}

Dans config.yml:
\begin{verbatim}
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: KGK\UserBundle\Entity\User
    registration:
        form:
            type: kgk_user_registration
            handler: kgk_user.form.handler.registration
\end{verbatim}

Enfin, on 'override' le contrôleur RegistrationController, qu'on a copié depuis le FOSUserBundle dans le dossier Controller de notre bundle. On modifie les arguments des fonctions du form handler pour ajouter le mot de passe généré dans le contrôleur. Le contrôleur fait appel au form handler et lui fournit le mot de passe généré. On encode ensuite le mot de passe pour l'ajouter dans la base.

On a aussi besoin d'afficher le mot de passe généré à la création du compte. On crée donc un nouveau fichier twig qui est la nouvelle vue du contrôleur RegistrationController. La raison pour laquelle le contrôleur doit utiliser une vue personnalisée est que seul le contrôleur connait le mot de passe non encodé. On ne peut pas récupérer un mot de passe depuis la base de donnée (mesure de sécurité élémentaire).



\subsection{Traduction française du bundle}
Dans le fichier app/config/config.yml, on prend garde à activer le translator pour activer l'utilisation des fichiers yaml de traduction dans les bundles qui en sont munis. Puis on copie le fichier de traduction française du bundle dans le dossier Resources de notre Bundle.

Ainsi le Bundle est traduit automatiquement en français, et on peut modifier les traductions dans ce fichier yaml. Ce genre de système pourrait nous permettre de réaliser un site multilingue aisément.

\subsection{Promotion d'un utilisateur}
Élever un utilisateur au rang d'administrateur se fait très facilement avec le FOSUserBundle.
On exécute la commande suivante:
\begin{verbatim}
php app/console fos:user:promote
\end{verbatim}
Ensuite, le prompt nous demande le nom d'utilisateur, puis le rôle à ajouter. Dans notre cas, on rentre ROLE\_ADMIN.
Pour vérifier que l'utilisateur est bien administrateur, on peut vérifier qu'apparait en haut à droite de la page le bouton "administration".

\clearpage

\section{StoreBundle}
\subsection{Base de donnée et modèles}
Il est très important d'avoir un schéma de base de données clair et adapté. La base de données a été conçue avec le logiciel PowerAmc; ainsi, on obtient rapidement un schéma explicite et cohérent.\\
On retrouve ci-dessous le modèle conceptuel de données et le modèle physique de données.

\begin{figure}[h]
\includegraphics[width=16cm]{./Images/MCD_image.png}
\label{MCD}
\caption{Modèle Conceptuel de Données}
\end{figure}

\begin{figure}[h]
\includegraphics[height=10cm]{./Images/MPD_image.png}
\label{MPD}
\caption{Modèle Physique de Données}
\end{figure}

\clearpage

\subsubsection{Choix de conception}
Nous avons créé un attribut "commande\_prix" qui correspond au montant de la commande. Théoriquement il peut être retrouvé via calcul et donc ne devrait pas figurer dans la base (cf règles de normalisation). Cependant cela accélère les requêtes lors de la consultation des commandes. En outre, si d'aventure on souhaitait proposer des promotions ou des remises exceptionnelles, il serait plus facile de modifier directement ce prix.\\

Concernant les commandes pour raisons de simplicité nous avons un attribut "commande\_etat" qui correspond au statut "en cours/validée/archivée". Cependant pour un site gérant beaucoup de commandes il serait préférable d'avoir une copie de la table qui contiendrait les archives des commandes.\\

De même pour pouvoir proposer plusieurs photos par produit il faudrait créer une table à part.

\subsubsection{Entities et Bundle}
L'ORM Doctrine, étroitement liée au framework Symfony, permet de maintenir à jour la base de donnée grâce à des entités, qui sont des classes PHP définies dans le dossier Entity de chaque Bundle. Une entité correspond à une table de la base, et Doctrine met à notre disposition des outils de génération d'entités.

Comme nous disposions déjà de la base de données grâce à PowerAmc, nous avons donc décidé de générer les entités à partir de la base existante. De plus, la génération d'entités génère également les setters et les getters des attributs.

\subsection{Entities et Store Bundle}
Le Store Bundle gère toute la partie marchande du site. Ainsi, il contient toutes les tables de la base, sauf la table utilisateur qui est contenue dans le FOSUserBundle.
La liste des entités du Store Bundle et la suivante:
\begin{itemize}
\item Categorie.php
\item Commande.php
\item Produit.php
\end{itemize}

\vspace{1em}



\clearpage


subsection{Interface administrateur}
Nous n'avons pas complété toutes les exigences du projet, notamment pour l'interface administrateur qui reste très simple. Elle consiste en deux parties:
\begin{itemize}
\item modification des produits
\item modification des catégories
\end{itemize}

Dans le fichier app/config/security.yml, on ajoute la ligne suivante:
\begin{verbatim}
- { path: ^/admin/, role: ROLE_ADMIN }
\end{verbatim}
L'accès à l'interface administrateur est alors restreint aux utilisateurs administrateurs. 

On peut modifier, supprimer et ajouter des produits et catégories.

\subsection{Panier}
Le panier ne fonctionne pas, nous nous y sommes mis trop tard.

\section{Design du site}

\subsection{Réalisation d'une maquette}
Avant de réaliser le design du site, nous avons créé une maquette très simple à l'aide du logiciel Dia :

\begin{center}
\begin{figure}[h]
\includegraphics[height=10cm]{./Images/maquette_store_accueil}
\label{Maquette}
\caption{Maquette du design}
\end{figure}
\end{center}

\clearpage

\subsection{Réalisation de la bannière}
La bannière du site a été réalisée avec le logiciel Blender (ogiciel d'animation 3D). Il s'agit d'un fichier gif.


\subsection{Twig: moteur de templates}

L'apparence du site est gérée par Twig.
Tous les templates twig du site incluent le fichier \emph{base.html.twig} situé dans le dossier \emph{app/config/Resources/views}.
Ainsi, l'harmonisation du design du site est aisée.

\subsection{La Sidebar}

\subsubsection{Extension twig de récupération des catégories}

La réalisation de la sidebar pose un problème lors de la récupération de la liste des catégories, qui devrait être fournie par tous les contrôleurs au moteur Twig.
Cela ne serait pas très pratique, aussi nous avons décidé de réaliser une extension twig dont le seul but est de donner l'accès à la liste des catégories au fichier twig \emph{base.html.twig}.

Cette extension est située dans le répertoire \emph{Extension/} du StoreBundle et est déclarée en tant que service dans le fichier \emph{config.yml}. Elle permet l'appel de la fonction \emph{getCategories()}, qui renvoie la liste des catégories récupérée directement depuis la base de données.

\clearpage

\end{document}
